package com.allwees.bs.c.common.aop;

import com.google.common.collect.Range;
import com.allwees.bs.c.module.app.dto.AdsVO;
import com.allwees.bs.c.module.ops.service.AdvertisementService;
import com.allwees.bs.core.model.ops.AdsModel;
import com.allwees.bs.core.model.ops.constant.AdPositionEnum;
import com.allwees.bs.core.modelbase.page.PageParams;
import com.allwees.bs.core.modelbase.page.PaginateDTO;
import com.allwees.bs.core.modelbase.vo.R;
import com.allwees.core.common.constant.EPlatform;
import com.allwees.core.common.util.http.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author Daniel
 * @Version 1.0.0
 * @Description
 * @CreateTime 2020/10/14 15:58
 */
@SuppressWarnings("rawtypes")
@Slf4j
@Component
@Aspect
public class InsertAdsAspect {

    @Resource
    private AdvertisementService advertisementService;

    @Pointcut("@annotation(com.allwees.bs.core.model.ops.annotation.InsertAds)")
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result = pjp.proceed();
        Object[] args = pjp.getArgs();
        String categoryNo = (String)args[0];
        if(!"P01".equals(categoryNo)){
            return result;
        }

        Object data = ((R) result).get("data");
        if(data == null){
            return result;
        }

        List results = ((PaginateDTO) data).getResults();
        if(CollectionUtils.isEmpty(results)){
            return result;
        }
        process(results,(PageParams) args[1]);
        return result;
    }

    private void process(List results,PageParams param) {
        EPlatform platform = RequestContext.getPlatform();
        if (platform == EPlatform.UNKNOWN){
            return;
        }
        AdsModel model = new AdsModel();
        model.setPlatform(platform);
        model.setPosition(AdPositionEnum.PRODUCT_LIST);
        model.setChannelUuid(RequestContext.getChannel());
        List<AdsVO> ads = advertisementService.list(model);
        if (CollectionUtils.isEmpty(ads)) {
            return;
        }
        Map<Integer, AdsVO> insertPositionMap = ads.stream().collect(Collectors.toMap(AdsVO::getSort, e -> e));
        Range<Integer> insertableRange = getInsertableRange(results, param);

        //从后往前插入，避免插入带来的位置偏移的影响
        List<Integer> insertPositions = insertPositionMap
                                            .keySet()
                                            .stream()
                                            .filter(insertableRange::contains)
                                            .sorted(Comparator.comparingInt(Integer::intValue).reversed())
                                            .collect(Collectors.toList());

        for (Integer insertPosition : insertPositions) {
            results.add(getInsertIndex(insertPosition,param),insertPositionMap.get(insertPosition));
        }
    }

    private int getInsertIndex(Integer insertPosition,PageParams param){
        return (insertPosition - 1) % param.getSize();
    }

    private Range<Integer> getInsertableRange(List<?> results,PageParams param){
        int size = results.size();
        int fromIndex = (param.getCurrent() - 1) * param.getSize();
        int startPosition = fromIndex + 1;
        int endPosition = fromIndex + size;
        return Range.closed(startPosition, endPosition);
    }
}
